package com.fmy.mcsecurity.config.filter;

import com.fmy.mcsecurity.commons.resp.SecurityResp;
import com.fmy.mcsecurity.commons.unusual.RefreshTokenException;
import com.fmy.mcsecurity.login.service.RefreshTokenService;
import com.fmy.mcsecurity.token.TokenManager;
import com.fmy.mcsecurity.token.model.RefreshStorage;
import org.apache.logging.log4j.util.Strings;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author Mr.fmy
 * @version 1.0.0
 * @ClassName RefreshTokenFilter
 * @Description TODO  RefreshToken filter
 * @createTime 2019/10/23 14:20
 */
public class RefreshTokenFilter extends OncePerRequestFilter {

    private final RefreshTokenService refreshTokenService;
    private final AuthenticationSuccessHandler successHandler;
    private final AuthenticationFailureHandler failureHandler;
    private final TokenManager tokenManager;

    private AntPathRequestMatcher matcher = new AntPathRequestMatcher("/login/refreshToken");
    private String refresh = "refreshToken";


    public RefreshTokenFilter(RefreshTokenService refreshTokenService, AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler, TokenManager tokenManager) {
        this.refreshTokenService = refreshTokenService;
        this.successHandler = successHandler;
        this.failureHandler = failureHandler;
        this.tokenManager = tokenManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (requiresAuthentication(request)) {
            try {
                String refreshStr = request.getParameter(refresh);
                if (Strings.isBlank(refreshStr)) throw new RefreshTokenException(SecurityResp.TOKEN_REFRESH_NULL_ERROR);
                RefreshStorage refreshStorage = tokenManager.getRefreshStorage(refreshStr);
                if (refreshStorage == null) throw new RefreshTokenException(SecurityResp.TOKEN_REFRESH_EXPIRE);
                UsernamePassParamsHolder.setClient(refreshStorage.getClient());
                UsernamePassParamsHolder.setLoginType(refreshStorage.getType());
                Authentication authentication = refreshTokenService.refresh(refreshStorage.getUserId());
                successHandler.onAuthenticationSuccess(request, response, authentication);
            } catch (AuthenticationException e) {
                failureHandler.onAuthenticationFailure(request, response, e);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                failureHandler.onAuthenticationFailure(request, response, new RefreshTokenException(e.getMessage(), e.getCause(), SecurityResp.TOKEN_REFRESH_UNKNOWN_ERROR));
            }
        } else {
            doFilter(request, response, chain);
        }
    }

    public void setMatcher(AntPathRequestMatcher matcher) {
        Assert.notNull(matcher, "matcher must not be null");
        this.matcher = matcher;
    }

    public void setRefresh(String refresh) {
        Assert.hasText(refresh, "refresh parameter must not be empty or null");
        this.refresh = refresh;
    }

    private boolean requiresAuthentication(HttpServletRequest request) {
        return matcher.matches(request);
    }
}
